package com.icloud.article.boot.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.icloud.article.boot.dao.ArticleDao;
import com.icloud.article.boot.dto.*;
import com.icloud.article.boot.mapper.ArticleBrowsedMapper;
import com.icloud.article.boot.model.*;
import com.icloud.article.boot.dto.AdminArticlePageDto;
import com.icloud.article.boot.query.ArticleQuery;
import com.icloud.article.boot.service.*;
import com.icloud.article.boot.vo.*;
import com.icloud.common.core.api.ResultCode;
import com.icloud.common.database.enums.*;
import com.icloud.common.database.mapper.*;
import com.icloud.common.database.model.*;
import com.icloud.common.web.enums.StateEnum;
import com.icloud.common.web.exception.Asserts;
import com.icloud.common.web.util.AuthUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.WeekFields;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 帖子service 实现类
 */

@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {
    @Autowired
    private AuthUtil authUtil;
    @Autowired
    private AttachmentService attachmentService;
    @Autowired
    private ArticleDao articleDao;
    @Autowired
    private UpvoteService upvoteService;
    @Autowired
    private DiscussService discussService;
    @Autowired
    private CollectService collectService;
    @Autowired
    private HotArticleService hotArticleService;
    @Autowired
    private ArticleVisibleCustomerService articleVisibleCustomerService;
    @Autowired
    private IMService imService;
    @Autowired
    private AppCustomerMapper appCustomerMapper;
    @Autowired
    private CustomerSettingsMapper customerSettingsMapper;
    @Autowired
    private CustomerWalletMapper customerWalletMapper;
    @Autowired
    private TWalletJournalMapper tWalletJournalMapper;
    @Autowired
    private ArticleSettingsMapper articleSettingsMapper;
    @Autowired
    private AttentionMapper attentionMapper;
    @Autowired
    private RewardService rewardService;
    @Autowired
    private ArticleBrowsedMapper articleBrowsedMapper;
    @Autowired
    private Executor asyncExecutor;

    /**
     * 发布帖子
     * @param dto
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public boolean publish(ArticleDto dto) {
        String visible = dto.getVisible();
        ArticleVisibleScopeEnum articleVisibleScopeEnum = ArticleVisibleScopeEnum.byCode(visible);
        Asserts.fail(null == articleVisibleScopeEnum, ResultCode.VALIDATE_FAILED);

        String permDown = dto.getPermDown();
        TFEnum permDownEnum = TFEnum.byCode(permDown);
        Asserts.fail(null == permDownEnum, ResultCode.VALIDATE_FAILED);

        String permTransmit = dto.getPermTransmit();
        TFEnum permTransmitEnum = TFEnum.byCode(permTransmit);
        Asserts.fail(null == permTransmitEnum, ResultCode.VALIDATE_FAILED);

        Asserts.fail(ArticleVisibleScopeEnum.FORBIDDEN.getCode().equals(dto.getVisible()) && CollectionUtil.isEmpty(dto.getVisibleCustomerIds()),ResultCode.VALIDATE_FAILED);

        String attachmentType = dto.getAttachmentType();
        AttachmentTypeEnum attachmentTypeEnum = AttachmentTypeEnum.byCode(attachmentType);
        Asserts.fail(null==attachmentTypeEnum, ResultCode.VALIDATE_FAILED);

        Long loginId = authUtil.getUserId();
        LambdaQueryWrapper<CustomerSettings> csQuery = new LambdaQueryWrapper<>();
        csQuery.eq(CustomerSettings::getCustomerId, loginId);
        CustomerSettings cs = customerSettingsMapper.selectOne(csQuery);
        Asserts.fail(cs!=null && TFEnum.isTrue(cs.getSilence()), ResultCode.HAS_BEEN_BANNED);

        LambdaQueryWrapper<ArticleSettings> asQuery = new LambdaQueryWrapper<>();
        ArticleSettings articleSettings = articleSettingsMapper.selectOne(asQuery);
        Asserts.fail(null == articleSettings, ResultCode.DATA_NOT_EXISTS);
        Integer publishRewardInt = articleSettings.getPublishReward();
        BigDecimal publishReward = new BigDecimal(publishRewardInt);

        LambdaQueryWrapper<CustomerWallet> cwQuery = new LambdaQueryWrapper<>();
        cwQuery.eq(CustomerWallet::getCustomerId, loginId);
        CustomerWallet cw = customerWalletMapper.selectOne(cwQuery);
        Asserts.fail(cw==null, ResultCode.DATA_NOT_EXISTS);

        String discussion = dto.getDiscussion();
        if(StringUtils.isBlank(discussion)){
            discussion = DiscussionEnum.ALL.getCode();
        }

        Article article = new Article();
        BeanUtils.copyProperties(dto, article);
        dto.setDiscussion(discussion);

        article.setRiskCtl(TFEnum.FALSE.getCode());
        article.setCreatedId(loginId);

        save(article);

        //保存附件
        attachmentService.batchAdd(article.getId(), dto.getAttachmentList());

        // 仅保存不让他看见的客户Id
        if(ArticleVisibleScopeEnum.FORBIDDEN.getCode().equals(article.getVisible())){
            articleVisibleCustomerService.batchAddForbidden(article.getId(),article.getCreatedId(),dto.getVisibleCustomerIds());
        }

        // 发布奖励
        BigDecimal publishBefore = cw.getBcpc();
        BigDecimal publishAfter = publishBefore.add(publishReward);

        TWalletJournal publishJournal = TWalletJournal.builder()
                .customerId(loginId)
                .journalType(JournalTypeEnum.PUBLISH_REWARD.getCode())
                .beforeBalance(publishBefore)
                .amount(publishReward)
                .afterBalance(publishAfter)
                .currency(CurrencyEnum.BCPC.getCode())
                .direction(DirectionEnum.DEBIT.getCode())
                .journalState(JournalStateEnum.FINISHED.getCode())
                .remark("发布奖励")
                .createdId(loginId)
                .updatedId(loginId)
                .build();
        tWalletJournalMapper.insert(publishJournal);

        CustomerWallet pubUpdateWallet = CustomerWallet.builder()
                .id(cw.getId())
                .bcpc(publishAfter)
                .build();
        customerWalletMapper.updateById(pubUpdateWallet);
        return true;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public boolean forward(ForwardDto dto) {
        Long oriArticleId = dto.getId();

        Article originalArticle = baseMapper.selectById(oriArticleId);
        Asserts.fail(null == originalArticle, ResultCode.DATA_NOT_EXISTS);

        // 是否禁止分享(转发)
        String oriPermTransmit = originalArticle.getPermTransmit();
        TFEnum tfEnum = TFEnum.byCode(oriPermTransmit);
        Asserts.fail(null == tfEnum || TFEnum.isTrue(oriPermTransmit), ResultCode.CANNOT_TRANSMIT);

        Long loginId = authUtil.getUserId();
        LambdaQueryWrapper<CustomerSettings> csQuery = new LambdaQueryWrapper<>();
        csQuery.eq(CustomerSettings::getCustomerId, loginId);
        CustomerSettings cs = customerSettingsMapper.selectOne(csQuery);
        Asserts.fail(cs!=null && TFEnum.isTrue(cs.getSilence()), ResultCode.HAS_BEEN_BANNED);

        Article newArticle = new Article();
        BeanUtils.copyProperties(originalArticle, newArticle);
        newArticle.setId(null);
        newArticle.setOriginalId(oriArticleId);
        newArticle.setForwardId(loginId);

        save(newArticle);

        Long newArticleId = newArticle.getId();

        //查询附件
        List<Attachment> oriAttachmentList = attachmentService.list(new LambdaQueryWrapper<Attachment>().eq(Attachment::getArticleId, oriArticleId));
        List<AttachmentDto> oriAttachmentDtoList = oriAttachmentList.stream().map(AttachmentDto::new).collect(Collectors.toList());
        //保存附件
        attachmentService.batchAdd(newArticleId, oriAttachmentDtoList);

        //保存不让他看见的客户Id
        if(ArticleVisibleScopeEnum.FORBIDDEN.getCode().equals(newArticle.getVisible())){
            List<ArticleVisibleCustomer> list = articleVisibleCustomerService.list(new LambdaQueryWrapper<ArticleVisibleCustomer>()
                    .eq(ArticleVisibleCustomer::getArticleId, oriArticleId));

            List<Long> visibleCustomerIds = list.stream().map(ArticleVisibleCustomer::getVisibleId).collect(Collectors.toList());
            articleVisibleCustomerService.batchAddForbidden(newArticleId, newArticle.getCreatedId(), visibleCustomerIds);
        }

        // 保存评论
        String content = dto.getContent();
        if(StringUtils.isNotBlank(content)){
            DiscussDto discussDto = new DiscussDto();
            discussDto.setArticleId(newArticleId);
            discussDto.setContent(content);
            discussService.discuss(discussDto);
        }
        return true;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public boolean cancelForward(CancelForwardDto dto) {
        Long articleId = dto.getId();
        Long originalId = dto.getOriginalId();
        Long forwardId = dto.getForwardId();
        Long loginId = authUtil.getUserId();
        Asserts.fail(!loginId.equals(forwardId), ResultCode.VALIDATE_FAILED);

        Article currentArticle = baseMapper.selectById(articleId);

        Asserts.fail(null==currentArticle, ResultCode.DATA_NOT_EXISTS);
        Asserts.fail(!originalId.equals(currentArticle.getOriginalId()), ResultCode.VALIDATE_FAILED);

        Article oriArticle = baseMapper.selectById(originalId);
        Asserts.fail(null==oriArticle, ResultCode.DATA_NOT_EXISTS);

        baseMapper.deleteById(articleId);
        return true;
    }

    /**
     * app用户删除帖子
     * @param id 帖子id
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public boolean delete(Long id) {
        Article article = getById(id);
        Asserts.fail(null == article,ResultCode.DATA_NOT_EXISTS);

        Long loginId = authUtil.getUserId();
        // 只有帖子的创建人才能删除帖子
        Asserts.fail(!loginId.equals(article.getCreatedId()), ResultCode.FORBIDDEN);

        baseMapper.deleteById(article);

        // 删除收藏
        LambdaQueryWrapper<Collect> collectQuery = new LambdaQueryWrapper<>();
        collectQuery.eq(Collect::getArticleId, id);
        List<Collect> collectList = collectService.list(collectQuery);
        if(!CollectionUtil.isEmpty(collectList)){
            collectService.removeBatchByIds(collectList);
        }

        // 删除点赞
        LambdaQueryWrapper<Upvote> upvoteQuery = new LambdaQueryWrapper<>();
        upvoteQuery.eq(Upvote::getBusinessId, id);
        upvoteQuery.eq(Upvote::getBusinessType, BusinessTypeEnum.ARTICLE.getCode());
        List<Upvote> upvoteList = upvoteService.list(upvoteQuery);
        if(!CollectionUtil.isEmpty(upvoteList)){
            upvoteService.removeBatchByIds(upvoteList);
        }

        // 删除评论
        LambdaQueryWrapper<Discuss> discussQuery = new LambdaQueryWrapper<>();
        discussQuery.eq(Discuss::getArticleId, id);
        List<Discuss> discussList = discussService.list(discussQuery);
        if(!CollectionUtil.isEmpty(discussList)){
            discussList.forEach(dis->{
                discussService.delete(dis.getId());
            });
        }
        return true;
    }


    /**
     * 后台分页
     * @param dto
     * @return
     */
    @Override
    public IPage<AdminArticleVo> adminPage(AdminArticlePageDto dto) {
        String nickname = dto.getNickname();
        List<Long> customerIdList = null;
        if(StringUtils.isNotBlank(nickname)) {
            LambdaQueryWrapper<AppCustomer> customerQuery = new LambdaQueryWrapper<>();
            customerQuery.like(AppCustomer::getNickname, nickname);
            customerQuery.eq(AppCustomer::getState, StateEnum.NORMAL.getCode());
            List<AppCustomer> customerList = appCustomerMapper.selectList(customerQuery);
            customerIdList = customerList.stream().map(AppCustomer::getId).collect(Collectors.toList());
        }
        IPage<AdminArticleVo> articleIPage = Page.of(dto.getCurrent(), dto.getPageSize());
        LocalDateTime now = LocalDateTime.now();
        String isHot = dto.getIsHot();
        List<Long> hotIdList = null;
        if(TFEnum.isTrue(isHot)){
            LambdaQueryWrapper<HotArticle> hotQuery = new LambdaQueryWrapper<>();
            hotQuery.lt(HotArticle::getExpireAt, now);
            Page<HotArticle> hotArticlePage = hotArticleService.page(Page.of(dto.getCurrent(), dto.getPageSize()), hotQuery);
            List<HotArticle> hotArticleList = hotArticlePage.getRecords();
            hotIdList = hotArticleList.stream().map(HotArticle::getArticleId).collect(Collectors.toList());
        }

        String isMember = dto.getIsMember();
        List<Long> memberIdList = null;
        if(TFEnum.isTrue(isMember)){
        }

        LocalDateTime queryBegin = dto.getCreatedAtBegin();
        LocalDateTime queryEnd = dto.getCreatedAtEnd();
        List<AdminArticleVo> adminArticleVoList = new ArrayList<>();

        LambdaQueryWrapper<Article> articleQuery = new LambdaQueryWrapper<Article>()
                .in(!CollectionUtils.isEmpty(customerIdList), Article::getCreatedId, customerIdList)
                .in(!CollectionUtils.isEmpty(hotIdList), Article::getId, hotIdList)
                .between(queryBegin!=null && queryEnd!=null, Article::getCreatedAt, queryBegin, queryEnd)
                .orderByDesc(Article::getUpdatedAt);
        Page<Article> articlePage = baseMapper.selectPage(Page.of(dto.getCurrent(), dto.getPageSize()), articleQuery);
        List<Article> articleList = articlePage.getRecords();
        if(CollectionUtil.isEmpty(articleList)){
            return articleIPage;
        }

        Set<Long> customerIdSet = articleList.stream().map(Article::getCreatedId).collect(Collectors.toSet());
        LambdaQueryWrapper<AppCustomer> customerQuery = new LambdaQueryWrapper<>();
        customerQuery.in(AppCustomer::getId, customerIdSet);
        customerQuery.eq(AppCustomer::getState, StateEnum.NORMAL.getCode());
        List<AppCustomer> customerList = appCustomerMapper.selectList(customerQuery);
        Map<Long, AppCustomer> customerMap = customerList.stream().collect(Collectors.toMap(AppCustomer::getId, Function.identity()));

        for(Article article : articleList){
            AdminArticleVo vo = new AdminArticleVo();
            adminArticleVoList.add(vo);

            Long customerId = article.getCreatedId();
            AppCustomer customer = customerMap.get(customerId);

            vo.setArticleId(article.getId());
            vo.setCreatedAt(article.getCreatedAt());
            vo.setRiskCtl(article.getRiskCtl());
            vo.setCustomerId(customerId);
            if(customer!=null){
                vo.setNickname(customer.getNickname());
            }
        }
        //构建vo
        buildAdminVo(adminArticleVoList);

        articleIPage.setRecords(adminArticleVoList);
        articleIPage.setTotal(articlePage.getTotal());
        return articleIPage;
    }


    /**
     * app端帖子列表
     * @param dto
     * @return
     */
    @Override
    public ArticlePageVo appPage(ArticlePageDto dto) {
        ArticlePageVo vo =  new ArticlePageVo();
        // 当customerId不为空
        Long createdId = dto.getCustomerId();
        Long loginId = authUtil.getUserId();
        String appScreenLocation = dto.getAppScreenLocation();
        AppScreenLocationEnum appScreenLocationEnum = AppScreenLocationEnum.byCode(appScreenLocation);
        if(appScreenLocationEnum==null){
            appScreenLocation = AppScreenLocationEnum.OTHER.getCode();
        }
        String articleQueryScope = dto.getArticleQueryScope();
        ArticleQueryScopeEnum articleQueryScopeEnum = ArticleQueryScopeEnum.byCode(articleQueryScope);
        if(articleQueryScopeEnum==null){
            articleQueryScope = ArticleQueryScopeEnum.ATTENTION.getCode();
        }
        String articleType = dto.getArticleType();
        ArticleTypeEnum articleTypeEnum = ArticleTypeEnum.byCode(articleType);
        if(articleTypeEnum==null){
            articleType = ArticleTypeEnum.ALL.getCode();
        }
        ArticleQuery query = new ArticleQuery();
        if(createdId!=null) {
            query.setCreatedId(createdId);
        }
        query.setArticleType(articleType);
        query.setLoginId(loginId);
        query.setAppScreenLocation(appScreenLocation);

        // 查询总贴数
        Long total = articleDao.countMyTotal(query);
        vo.setTotal(total);

        // 仅查询关注的人的帖子
        if(ArticleQueryScopeEnum.isAttention(articleQueryScope)){
            LambdaQueryWrapper<Attention> attentionQuery = new LambdaQueryWrapper<>();
            attentionQuery.select(Attention::getFollowedId);
            attentionQuery.eq(Attention::getCreatedId, loginId);
            List<Attention> followedList = attentionMapper.selectList(attentionQuery);
            if(!CollectionUtil.isEmpty(followedList)){
                List<Long> tempIdList = followedList.stream().map(Attention::getFollowedId).collect(Collectors.toList());
                query.setFollowedIdList(tempIdList);
            }
        }

        // 不让他(当前登录人)看,包括原创与转发
        List<ArticleVisibleCustomer> articleVisibleCustomers = articleVisibleCustomerService.list(new LambdaQueryWrapper<ArticleVisibleCustomer>()
                .eq(ArticleVisibleCustomer::getVisibleId, loginId)
                .eq(ArticleVisibleCustomer::getIsVisible, ArticleVisibleEnum.FALSE.getCode())
        );
        if(CollectionUtil.isNotEmpty(articleVisibleCustomers)){
            Set<Long> forbiddentArticleIdSet = articleVisibleCustomers.stream().map(ArticleVisibleCustomer::getArticleId).collect(Collectors.toSet());
            query.setForbiddenIds(forbiddentArticleIdSet);
        }

        IPage<ArticleVo> page = articleDao.appPage(new Page<>(dto.getCurrent(), dto.getPageSize()), query);
        List<ArticleVo> records = page.getRecords();
        if(CollectionUtil.isEmpty(records)){
            return vo;
        }

        // 自己的
        List<ArticleVo> selfList = records.stream().filter(r->r.getCreatedId().equals(loginId)).collect(Collectors.toList());

        Map<Long, Long> strangerVisibleMap= records.stream()
                .filter(r->ArticleVisibleScopeEnum.isStranger(r.getDiscussion()))
                .collect(Collectors.toMap(ArticleVo::getId, ArticleVo::getCreatedId));
        if(!strangerVisibleMap.isEmpty()){
            List<Long> forbiddenList =  new ArrayList<>();
            strangerVisibleMap.forEach((key, value) -> {
                PullFriendDto pullFriendDto = new PullFriendDto();
                pullFriendDto.setUserId(value.toString());
                List<String> friendIdList = imService.pullFriends(pullFriendDto);
                if (!CollectionUtil.isEmpty(friendIdList)) {
                    List<Long> friendList = friendIdList.stream().filter(StringUtils::isNumeric).map(Long::valueOf).collect(Collectors.toList());
                    if (!CollectionUtil.isEmpty(friendList)) {
                        // 不可见的article ids
                        if (friendList.contains(loginId)) {
                            forbiddenList.add(key);
                        }
                    }
                }
            });
            // 剔除不可见的
            records = records.stream().filter(r->!forbiddenList.contains(r.getId())).collect(Collectors.toList());
        }
        // 剔除风控的
        List<ArticleVo> riskCtlList = records.stream().filter(r->TFEnum.isTrue(r.getRiskCtl()) && !r.getCreatedId().equals(loginId)).collect(Collectors.toList());
        records.removeAll(riskCtlList);

        // 保留自己的(自己发的,无论是什么可见范围,都可见)
        records.addAll(selfList);
        records = records.stream().distinct().collect(Collectors.toList());

        // 剔除自己分享的
        records = records.stream().filter(r->!loginId.equals(r.getForwardId())).collect(Collectors.toList());

        page.setSize(records.size());
        page.setRecords(records);

        //构建vo
        build(records);

        vo.setVoPage(page);
        return vo;
    }

    @Override
    public ArticleBrowsedPageVo browsedPage(ArticleBrowsedPageDto dto) {
        LocalDate today = LocalDate.now();
        LocalDateTime todayBegin = today.atTime(LocalTime.MIN);
        LocalDateTime todayEnd = today.atTime(LocalTime.MAX);

        LocalDate yesterday = today.minusDays(1);
        LocalDateTime yesterdayBegin = yesterday.atTime(LocalTime.MIN);
        LocalDateTime yesterdayEnd = yesterday.atTime(LocalTime.MAX);

        WeekFields weekFields= WeekFields.ISO;
        LocalDateTime weekBegin = today.with(weekFields.dayOfWeek(), 1L).atTime(LocalTime.MIN);
        LocalDateTime weekEnd = today.with(weekFields.dayOfWeek(), 7L).atTime(LocalTime.MAX);

        ArticleBrowsedPageVo vo = new ArticleBrowsedPageVo();
        Long customerId = dto.getCustomerId();

        IPage<ArticleBrowsed> abIPage = Page.of(dto.getCurrent(), dto.getPageSize());
        LambdaQueryWrapper<ArticleBrowsed> browsedQuery = new LambdaQueryWrapper<>();
        browsedQuery.eq(ArticleBrowsed::getBrowserId, customerId);
        IPage<ArticleBrowsed> abPage = articleBrowsedMapper.selectPage(abIPage, browsedQuery);
        List<ArticleBrowsed> abList = abPage.getRecords();
        if(CollectionUtil.isEmpty(abList)){
            return vo;
        }
        List<Long> articleIdList = abList.stream().map(ArticleBrowsed::getArticleId).collect(Collectors.toList());
        List<Article> articleList = baseMapper.selectBatchIds(articleIdList);
        Map<Long, Article> articleMap = articleList.stream().collect(Collectors.toMap(Article::getId, Function.identity()));

        LambdaQueryWrapper<Attachment> attQuery = new LambdaQueryWrapper<>();
        attQuery.in(Attachment::getArticleId, articleIdList);
        List<Attachment> attachmentList = attachmentService.list(attQuery);
        Map<Long, List<Attachment>> attMap = attachmentList.stream().collect(Collectors.groupingBy(Attachment::getArticleId));

        List<Long> createdIdList = articleList.stream().map(Article::getCreatedId).collect(Collectors.toList());
        List<AppCustomer> createdList = appCustomerMapper.selectBatchIds(createdIdList);
        Map<Long, AppCustomer> createdMap = createdList.stream().collect(Collectors.toMap(AppCustomer::getId, Function.identity()));

        List<ArticleBrowsedVo> abVoList = new ArrayList<>();
        for(ArticleBrowsed ab : abList){
            Article article = articleMap.get(ab.getArticleId());
            if(article==null){
                continue;
            }
            Long createdId = article.getCreatedId();
            AppCustomer created = createdMap.get(createdId);
            List<Attachment> attList = attMap.get(article.getId());
            ArticleBrowsedVo abVo = new ArticleBrowsedVo();
            abVo.setId(ab.getId());
            abVo.setArticleId(article.getId());
            abVo.setContent(article.getContent());
            abVo.setAttachmentList(attList);
            if(created!=null){
                abVo.setCreatedId(createdId);
                abVo.setCreatedAt(article.getCreatedAt());
                abVo.setCreatedNickname(created.getNickname());
                abVo.setCreatedAvatarUrl(created.getAvatarUrl());
            }
            abVoList.add(abVo);
        }

        List<ArticleBrowsedVo> todayList = abVoList.stream().filter(ab->ab.getCreatedAt().isAfter(todayBegin) && ab.getCreatedAt().isBefore(todayEnd)).collect(Collectors.toList());
        List<ArticleBrowsedVo> yesterdayList = abVoList.stream().filter(ab->ab.getCreatedAt().isAfter(yesterdayBegin) && ab.getCreatedAt().isBefore(yesterdayEnd)).collect(Collectors.toList());
        List<ArticleBrowsedVo> weekList = abVoList.stream().filter(ab->ab.getCreatedAt().isAfter(weekBegin) && ab.getCreatedAt().isBefore(weekEnd)).collect(Collectors.toList());
        vo.setToday(todayList);
        vo.setYesterday(yesterdayList);
        vo.setInWeek(weekList);
        return vo;
    }

    /**
     * 查询详情
     * @param id
     * @return
     */
    @Override
    public ArticleVo load(Long id) {
        Article article = getById(id);
        Asserts.fail(null==article, ResultCode.DATA_NOT_EXISTS);

        // 查询发帖人信息
        Long createdId = article.getCreatedId();
        AppCustomer appCustomer = appCustomerMapper.selectById(createdId);
        Asserts.fail(null==appCustomer, ResultCode.DATA_NOT_EXISTS);

        String nickname = appCustomer.getNickname();
        String avatarUrl = appCustomer.getAvatarUrl();

        ArticleVo vo = new ArticleVo();
        BeanUtil.copyProperties(article,vo);
        vo.setCreatedName(nickname);
        vo.setCreatedAvatarUrl(avatarUrl);

        Long loginId = authUtil.getUserId();
        // 浏览记录
        asyncExecutor.execute(()->{
            ArticleBrowsed ab = new ArticleBrowsed();
            ab.setArticleId(id);
            ab.setBrowserId(loginId);
            articleBrowsedMapper.insert(ab);
        });

        //查询点赞数量
        Long upvoteCountByArticle = upvoteService.queryUpvoteCount(BusinessTypeEnum.ARTICLE.getCode(), id);
        vo.setUpvoteCount(upvoteCountByArticle);

        //判断是否点赞
        long count = upvoteService.count(new LambdaQueryWrapper<Upvote>()
                .eq(Upvote::getBusinessId, id)
                .eq(Upvote::getBusinessType, 0)
                .eq(Upvote::getCreatedId, loginId));
        vo.setIsUpvote(count > 0);

        //查询评论数量
        Long discussCountByArticle = discussService.queryDiscussCountByArticle(id);
        vo.setDiscussCount(discussCountByArticle);

        // 查询转发数量
        LambdaQueryWrapper<Article> forwardQuery = new LambdaQueryWrapper<>();
        forwardQuery.eq(Article::getOriginalId, id);
        Long forwardCount = baseMapper.selectCount(forwardQuery);
        if(forwardCount==null){
            forwardCount = 0L;
        }
        vo.setForwardCount(forwardCount);

        //查询附件
        List<Attachment> attachmentList = attachmentService.list(new LambdaQueryWrapper<Attachment>().eq(Attachment::getArticleId, id));
        vo.setAttachmentList(attachmentList);

        // 查询关注信息
        LambdaQueryWrapper<Attention> attQuery = new LambdaQueryWrapper<>();
        attQuery.eq(Attention::getCreatedId, loginId);
        attQuery.eq(Attention::getFollowedId, createdId);
        Attention attention = attentionMapper.selectOne(attQuery);
        if(attention!=null){
            attQuery = new LambdaQueryWrapper<>();
            attQuery.eq(Attention::getCreatedId, createdId);
            attQuery.eq(Attention::getFollowedId, loginId);
            Attention each = attentionMapper.selectOne(attQuery);
            if(each!=null) {
                vo.setAttention(AttentionEnum.EACH.getCode());
            }else{
                vo.setAttention(AttentionEnum.FOLLOWED.getCode());
            }
        }

        //查询打赏金额
        Map<Long, BigDecimal> rewardAmountByArticle = rewardService.queryRewardAmountByArticle(Arrays.asList(id));
        vo.setRewardAmount(rewardAmountByArticle.get(id));

        return vo;
    }


    /**
     * 根据ids查询vo
     * @param ids                       帖子ids
     * @return
     */
    @Override
    public List<ArticleVo> listVoByIds(Collection<Long> ids) {

        List<ArticleVo> list = articleDao.list(new LambdaQueryWrapper<Article>().in(Article::getId, ids));

        //构建vo
        build(list);
        return list;
    }

    /**
     * 构建帖子vo
     * @param articleVos                帖子列表
     */
    private void build(List<ArticleVo> articleVos){
        List<Long> articleIds = articleVos.stream().map(ArticleVo::getId).collect(Collectors.toList());

        //查询评论量
        Map<Long, Long> discussCountByArticle = discussService.queryDiscussCountByArticle(articleIds);

        //查询点赞数量
        Map<Long, Long> upvoteCountByArticle = upvoteService.queryUpvoteCount(BusinessTypeEnum.ARTICLE.getCode(),articleIds);

        //查询是否点赞
        Map<Long, Boolean> upvoteByArticle = upvoteService.queryUpvote(BusinessTypeEnum.ARTICLE.getCode(), articleIds);

        //查询附件
        Map<Long,List<Attachment>> attachmentByArticle = attachmentService.queryAttachmentCountByArticle(articleIds);

        //查询转发数量
        Map<Long, Long> forwardCountByArticle = queryforwardCountByArticle(articleIds);

        // 查询是否已收藏
        Map<Long, Boolean> collectByArticle = collectService.queryCollect(articleIds);


        for (ArticleVo articleVo : articleVos) {
            //评论数量
            Long discussCnt = discussCountByArticle.get(articleVo.getId());
            if(discussCnt!=null) {
                articleVo.setDiscussCount(discussCnt);
            }

            //点赞数量
            Long upvoteCnt = upvoteCountByArticle.get(articleVo.getId());
            if(upvoteCnt!=null) {
                articleVo.setUpvoteCount(upvoteCnt);
            }

            //查询是否点赞
            Boolean isUpvote = upvoteByArticle.get(articleVo.getId());
            articleVo.setIsUpvote(isUpvote);

            //附件列表
            articleVo.setAttachmentList(attachmentByArticle.get(articleVo.getId()));

            //转发次数
            Long forwardCnt = forwardCountByArticle.get(articleVo.getId());
            if(forwardCnt!=null) {
                articleVo.setForwardCount(forwardCnt);
            }

            // 是否已收藏
            Boolean isCollect = collectByArticle.get(articleVo.getId());
            articleVo.setIsCollect(isCollect);
        }
    }


    /**
     * 根据帖子id查询转发数量
     * @param articleId
     * @return key:帖子id，value:转发次数
     */
    private Map<Long,Long> queryforwardCountByArticle(List<Long> articleId){
        List<StatisticsVo> statisticsVos = articleDao.queryForwardCountByArticle(new LambdaQueryWrapper<Article>()
                .in(Article::getOriginalId, articleId)
                .groupBy(Article::getOriginalId));
        return statisticsVos.stream().collect(Collectors.toMap(StatisticsVo::getArticleId, StatisticsVo::getCount));
    }

    private void buildAdminVo(List<AdminArticleVo> articleVoList){
        List<Long> articleIds = articleVoList.stream().map(AdminArticleVo::getArticleId).collect(Collectors.toList());
        // 查询消耗量
        Map<Long, BigDecimal> hotConsumptionByArticle = hotArticleService.queryConsumption(articleIds);

        // 查询收藏量
        Map<Long, Long> collectByArticle = collectService.queryCollectCount(articleIds);

        // 查询点赞数量
        Map<Long, Long> upvoteCountByArticle = upvoteService.queryUpvoteCount(BusinessTypeEnum.ARTICLE.getCode(),articleIds);

        // 查询评论量
        Map<Long, Long> discussCountByArticle = discussService.queryDiscussCountByArticle(articleIds);

        for (AdminArticleVo adminArticleVo : articleVoList) {
            // 消耗量
            BigDecimal hotConsumption = hotConsumptionByArticle.get(adminArticleVo.getArticleId());
            if(hotConsumption!=null) {
                adminArticleVo.setHotConsumption(hotConsumption);
            }else{
                adminArticleVo.setHotConsumption(BigDecimal.ZERO);
            }
            // 收藏量
            Long collectCnt = collectByArticle.get(adminArticleVo.getArticleId());
            if(collectCnt!=null) {
                adminArticleVo.setCollectCount(collectCnt);
            }
            //评论数量
            Long discussCnt = discussCountByArticle.get(adminArticleVo.getArticleId());
            if(discussCnt!=null) {
                adminArticleVo.setDiscussCount(discussCnt);
            }
            //点赞数量
            Long upvoteCnt = upvoteCountByArticle.get(adminArticleVo.getArticleId());
            if(upvoteCnt!=null) {
                adminArticleVo.setUpvoteCount(upvoteCnt);
            }
        }
    }
}
